package com.vhall.component.service.interactive.impl;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.lang.UUID;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.vhall.component.dao.interactive.QuestionAnswersMapper;
import com.vhall.component.dao.interactive.QuestionRoomMapper;
import com.vhall.component.dao.interactive.QuestionsMapper;
import com.vhall.component.dao.interactive.RoomQuestionLkMapper;
import com.vhall.component.entity.interactive.entity.QuestionsEntity;
import com.vhall.component.entity.room.RoomQuestionLkEntity;
import com.vhall.component.entity.room.dto.RoomsEntityDTO;
import com.vhall.component.framework.common.exception.BusinessException;
import com.vhall.component.plugin.common.constant.RedisKey;
import com.vhall.component.plugin.common.enums.RoomConfigStatusEnum;
import com.vhall.component.plugin.common.exception.BizErrorCode;
import com.vhall.component.plugin.common.utils.JsonUtil;
import com.vhall.component.plugin.core.service.OssService;
import com.vhall.component.plugin.core.service.PaasService;
import com.vhall.component.service.notice.NoticesService;
import com.vhall.component.service.room.RoomInnerService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.transaction.annotation.Transactional;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author yongzheng.xu
 * @date 2021/5/13 13:41
 */
@Slf4j
public class QuestionBaseServiceImpl {

    @Autowired
    QuestionRoomMapper questionRoomMapper;

    @Autowired
    QuestionsMapper questionsMapper;

    @Autowired
    QuestionAnswersMapper questionAnswersMapper;

    @Autowired
    RoomQuestionLkMapper roomQuestionLkMapper;

    @Autowired
    PaasService paasService;

    @Autowired
    RedisTemplate<String, String> stringStringRedisTemplate;

    @Autowired
    RedisTemplate redisTemplate;

    @Autowired
    RoomInnerService roomInnerService;

    @Autowired
    NoticesService noticesService;

    @Autowired
    private OssService ossService;

    @Transactional(rollbackFor = Exception.class)
    public boolean doPublish(Integer accountId, String nickName,
                             String roomId, Integer questionId,
                             Integer roleName) {
        // 2、获取问卷信息。
        LambdaQueryWrapper<QuestionsEntity> where = Wrappers.lambdaQuery();
        where.eq(QuestionsEntity::getQuestionId, questionId)
                .last("limit 1");
        QuestionsEntity qe = questionsMapper.selectOne(where);
        if (qe == null) {
            throw new BusinessException(BizErrorCode.EMPTY_QUESTION);
        }
        if (qe.getDeletedAt() != null) {
            throw new BusinessException(BizErrorCode.DELETED_QUESTION);
        }

        // 3、获取房间信息。
        RoomsEntityDTO re = roomInnerService.getRoomInfo(roomId);
        if (re == null) {
            throw new BusinessException(BizErrorCode.EMPTY_ROOM);
        }
        // 4、校验一下问卷里的account id和room里的account id是否相等。
        if (!(StringUtils.isNotBlank(qe.getAccountId()) &&
                ObjectUtils.isNotEmpty(re.getAccountId()))) {
            log.warn("问卷信息中的account id和房间信息中的account id不匹配！:{}{}", qe.getAccountId(), re.getAccountId());
            throw new BusinessException(BizErrorCode.EMPTY_QUESTION);
        }
        // 5、获取房间和问卷关联信息。
        LambdaQueryWrapper<RoomQuestionLkEntity> whereOfRqle = Wrappers.lambdaQuery();
        whereOfRqle.eq(RoomQuestionLkEntity::getQuestionId, questionId)
                .eq(RoomQuestionLkEntity::getRoomId, roomId)
                .last("limit 1");
        RoomQuestionLkEntity rqle = roomQuestionLkMapper.selectOne(whereOfRqle);
        if (rqle == null) {
            throw new BusinessException(BizErrorCode.COMP_QUESTION_ROOM_ALREADY_UNBIND);
        }

        if (rqle.getPublish() == 1) {
            throw new BusinessException(BizErrorCode.COMP_QUESTION_ROOM_ALREADY_PUBLISHED);
        }

        // 6、发布问卷，问卷信息本地静态化。
        String ossFileUrl = writeInfoLocal(questionId);

        // 7、如果上传成功就开始更新房间和问卷关联表。
        LambdaUpdateWrapper<RoomQuestionLkEntity> whereOfRql = Wrappers.lambdaUpdate();
        whereOfRql.set(RoomQuestionLkEntity::getPublish, 1)
                .set(RoomQuestionLkEntity::getUpdatedAt, LocalDateTime.now())
                .eq(RoomQuestionLkEntity::getQuestionId, questionId);
        int update = roomQuestionLkMapper.update(null, whereOfRql);
        if (update <= 0) {
            throw new BusinessException(BizErrorCode.COMP_QUESTION_PUBLISH_FAILED);
        }
        // 8、发送消息。
        Map<String, String> bodyParam = new HashMap<>(8);
        bodyParam.put("type", "questionnaire_push_guoxin");
        bodyParam.put("questionnaire_id", questionId.toString());
        bodyParam.put("nick_name", nickName);
        bodyParam.put("account_id", accountId.toString());
        bodyParam.put("room_role", roleName.toString());
        bodyParam.put("info_url", ossFileUrl);
        paasService.sendMessage(re.getChannelId(), JsonUtil.toJsonString(bodyParam), null, "service_room", "pc_browser", 1);
        // 9、记录redis,用于多房间同时消费
        redisTemplate.opsForSet().add(RedisKey.FORM_QUEUE_ROOM_IDS, roomId);
        // 10、发问卷发送公告信息
//        noticesService.sendNotice(roomId, questionId.toString(), accountId, "questionnaire");
        // 11、调查问卷上报(向单个用户推送) php代码直接返回了false；
        // 12、记录该房间发布过问卷
        stringStringRedisTemplate.opsForHash().put(RedisKey.INTERACT_TOOL_RECORDS + roomId, "is_question", String.valueOf(1));

        //设置问卷开关
        roomInnerService.updateConfigValueByRoomId(roomId, 1, RoomConfigStatusEnum.QUESTION_STATUS.getValue());
        return true;
    }

    public String writeInfoLocal(Integer questionId) {
        String ossFileUrl = "";
        try {
            /*
                问卷只能发布一次，后面只能是推屏，并且发布之后不能再修改问卷信息
                问卷在发布时，生成oss文件并保存至缓存中，后面推屏操作取缓存，减少表单服务调用
             */
            String key = RedisKey.QUESTION_INFO_OSS_URL + questionId.toString();
            String url = (String) redisTemplate.opsForValue().get(key);
            if (StringUtils.isNotEmpty(url)) {
                return url;
            }
            JSONObject jsonObject = paasService.getFormInfo(questionId.toString());
            String type = "text/plain";
            String name = "app/public/upload/question/" + questionId + ".json";
            // 本地先生成文件
            byte[] bytes = jsonObject.toString().getBytes(StandardCharsets.UTF_8);
            InputStream stream = new ByteArrayInputStream(bytes);
            ossFileUrl = ossService.upload(stream, name, type);

            redisTemplate.opsForValue().set(key, ossFileUrl, 60 * 60 * 24, TimeUnit.SECONDS);
        } catch (Exception e) {
            log.error("发布-写入文件错误!{}", e.getMessage());
        }
        return ossFileUrl;
    }


    public String writeIdsLocal(Integer questionId, JSONArray arr) {
        String idsFileUrl = "";
        try {
            String type = "text/plain";

            String name = "app/public/upload/accountids/"+ UUID.randomUUID().toString() + questionId + ".json";
            // 本地先生成文件
            byte[] bytes = arr.toString().getBytes(StandardCharsets.UTF_8);
            InputStream stream = new ByteArrayInputStream(bytes);
            idsFileUrl = ossService.upload(stream, name, type);

        } catch (Exception e) {
            log.error("发布-写入文件错误!{}", e.getMessage());
        }
        return idsFileUrl;
    }

    /**
     * 绑定问卷, 调用方法前, 确保 room id 存在
     *
     * @param questionId
     * @param accountId
     * @param roomId
     * @param finishTime
     * @return
     */
    public boolean bindRoom(Integer questionId, Integer accountId, String roomId, String finishTime) {

        QueryWrapper<RoomQuestionLkEntity> where = new QueryWrapper<>();
        where.eq("question_id", questionId);
        where.last("limit 1");
        RoomQuestionLkEntity lk = roomQuestionLkMapper.selectOne(where);
        RoomQuestionLkEntity entity = new RoomQuestionLkEntity();
        int effect = 0;
        if (null == lk) {
            entity.setQuestionId(questionId);
            entity.setAccountId(accountId);
            entity.setRoomId(roomId);
            entity.setBind(1);
            if (StringUtils.isNotBlank(finishTime)) {
                entity.setFinishTime(LocalDateTime.parse(finishTime, DateTimeFormatter.ofPattern(DatePattern.NORM_DATETIME_PATTERN)));
            }
            effect = roomQuestionLkMapper.insert(entity);
        } else {
            entity.setId(lk.getId());
            entity.setBind(1);
            effect = roomQuestionLkMapper.updateById(entity);
        }
        // 删除缓存。
        redisTemplate.delete(RedisKey.QUESTION_INFO_KEY + roomId + "and" + questionId);
        return effect == 1;
    }
}
